/*
 * client.c
 *
 *  Created on: 08-06-2012
 *      Author: Mateusz Matrejek
 */
#include "backup.h"
#include <string.h>
#include <stdlib.h>
#include <ftw.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <netdb.h>
#include <time.h>
#include <pthread.h>
#include "request.h"
#include "signal.h"
#include <openssl/md5.h>

unsigned char final[MD5_DIGEST_LENGTH];

struct ClientConfiguration config;
int socketFD;
time_t lastScan;

pthread_t listenThread;
pthread_t updateThread;


#include <openssl/md5.h>
#include <stdio.h>


int hash_compare(unsigned char a[MD5_DIGEST_LENGTH],unsigned char b[MD5_DIGEST_LENGTH] )
{
  int i;
        for (i=0;i<MD5_DIGEST_LENGTH;i++)
                if (a[i]!=b[i])
                        return -1;
        return 0;
}

void md5generate(FILE *f)
{
         const int bufferSize=1024;
         MD5_CTX ctx;
         MD5_Init(&ctx);
         unsigned char bufor[bufferSize];
         int newPart;

         while (!feof(f))
                   {
                        newPart=fread(bufor,1,bufferSize,f);
                        MD5_Update(&ctx,bufor,newPart);
                   }
         MD5_Final(final,&ctx);

         int i;
         for (i=0;i<MD5_DIGEST_LENGTH;i++)
             printf("%02x",final[i]);
         printf("\n");
}

void sigintHandler(int sig)
{
    struct WorkingRequest wReq;
    wReq.operation=REQ_LOGOUT;
    int n= write(socketFD,&wReq,sizeof(struct WorkingRequest));
    if (n<0)
      perror("Request sending error");

    exit(0);
}

void loadConfig(void)
{
  strcat(config.name,"Client1");
  strcat(config.serverHost,"localhost");
  strcat(config.path,"/home/student/Pulpit/myClient");
  config.port=6666;
  strcat(config.passphrase,"6d61746575737a6d617472656a656b");
}

int initialScanFunction(const char* file,const struct stat* st, int flag)
{
  printf("%s\n",file);
  struct WorkingRequest request;
  struct WorkingRequest response;
  request.operation=REQ_QUERY;
  strcpy(request.path,file);

  if (S_ISDIR(st->st_mode))
    {
      request.isDirectory=TRUE;
    }
  else
    {
      request.isDirectory=FALSE;
      request.size=st->st_size;

      FILE * ptr = fopen(file,"r");
      md5generate(ptr);
      memcpy(&request.hash,final,MD5_DIGEST_LENGTH*sizeof(unsigned char));
      fclose(ptr);
    }
  request.mtime=st->st_mtime;

  int n = write(socketFD,&request,sizeof(struct WorkingRequest));
  if (n < 0)
       perror("Blad write()");

  n = read(socketFD,&response,sizeof(struct WorkingRequest));
  if (n < 0)
       perror("Blad read()");

  char command[MAX_PATH_LENGTH];
  int fd;
  char buffer[response.size];
  switch(response.type){
            case(TYP_OK):
                break;
            case(TYP_REMVF):
                strcpy(command,"rm -rf ");
                strcat(command,config.path);
                strcat(command,response.path);
                if (system(command)!=0)
                  {
                  perror("Error while removing. Sync may be corrupted");
                  }
                break;
            case(TYP_SENDF):
                fd = open(response.path,O_RDONLY);
                read(fd,&buffer,response.size);
                write(socketFD,&buffer,response.size);
                close(fd);
                break;
            case(TYP_RECVF):
                fd = open(response.path,O_WRONLY);
                read(socketFD,&buffer,response.size);
                write(fd,&buffer,response.size);
                close(fd);
                break;
  };
  return 0;
}

void initialScan(void)
{
  if (ftw (config.path, initialScanFunction, 100) == -1)
    {
      perror("Error while initial scan");
      exit(1);
    }
  lastScan=time(NULL);
}

void updateRoutine(void)
{

}

void initializeConnection(void)
{
  struct sockaddr_in serv_addr;
  struct hostent *server;

  socketFD = socket(AF_INET, SOCK_STREAM, 0);
  if (socketFD < 0)
    {
      perror("Socket creation error");
      exit(1);
    }

  server = gethostbyname(config.serverHost);
  if (server == NULL)
    {
      perror("Unable to resolve given host");
      exit(1);
    }
  bzero((char *) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  bcopy((char *)server->h_addr,(char *)&serv_addr.sin_addr.s_addr,server->h_length);
  serv_addr.sin_port = htons(config.port);
  if (connect(socketFD,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
    {
      perror("Error while creating connection");
      exit(1);
    }

  struct LoginRequest req;
  strcpy(req.name,config.name);
  strcpy(req.passphrase,config.passphrase);

  int n = write(socketFD,&req,sizeof(struct LoginRequest));
  if (n < 0)
       perror("Blad write()");

  struct LoginResponse loginRes;

  n = read(socketFD,&loginRes,sizeof(struct LoginResponse));
  if (n < 0) perror("blad read()");

  switch (loginRes.response) {
          case RESP_ACCEPT:
            if (0!=signal(SIGINT,sigintHandler))
              perror("sign");
            break;
          case RESP_SFULL:
            errno=EPERM;
            perror("Server is full");
            exit(1);
          case RESP_DEINED:
            errno=EACCES;
            perror("Invalid passphrase");
            exit(1);
  }

}

int main(int argc, char **argv)
{
  loadConfig();
  initializeConnection();
  while (1)
    {
    initialScan();
    sleep(15);
    }
  return 0;
}
